package com.molichuxing.services.sdk.union;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.molichuxing.framework.utils.BigDecimalUtils;
import com.molichuxing.framework.utils.PropertiesUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.TreeMap;

/**
 * @Auther: yixuelin
 * @Date: 2018/8/21 13:28
 * @Description:
 */
public class UnionpayUtil {
    private static final Logger logger = LoggerFactory.getLogger(UnionpayUtil.class);
    private static final Map<String, String> properties = PropertiesUtil.getInstance().getAllProperties("application.properties");
    /**
     *
     * @Title: createOrderStr
     * @Description: 创建请求银联拼接的字符串并返回到客户端
     * @param orderId
     *            订单号
     * @param txnAmt
     *            订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
     */
	public static JSONObject createOrderStr(String txnAmt, String orderId, String txnTime, Integer payOrderTim) {
		payOrderTim = payOrderTim == null ? 15 : payOrderTim <= 0 ? 15 : payOrderTim;
        //total_amount = "0.01";//支付临时改为1分
        UnionpayConfig.getConfig().loadPropertiesFromSrc();
        Map<String, String> orderMap = new TreeMap<String, String>();
        /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
        orderMap.put("version", UnionpayConfig.getConfig().getVersion());   			  //版本号，全渠道默认值、
        orderMap.put("encoding", UnionpayConfig.getConfig().getEncoding()); 			  //字符集编码，可以使用UTF-8,GBK两种方式
        orderMap.put("signMethod", UnionpayConfig.getConfig().getSignMethod()); //签名方法
        orderMap.put("txnType", properties.get("union.sdk.txn.type"));  //交易类型 ，01：消费
        orderMap.put("txnSubType", properties.get("union.sdk.txn.sub.type"));//交易子类型， 01：自助消费
        orderMap.put("bizType", properties.get("union.biz.type"));           			  //业务类型，B2C网关支付，手机wap支付
        orderMap.put("channelType", properties.get("union.channel.type"));           			  //渠道类型，这个字段区分B2C网关支付和手机wap支付；07：PC,平板  08：手机
        /***商户接入参数***/
        orderMap.put("merId", properties.get("union.merid"));    	          			  //商户号码，请改成自己申请的正式商户号或者open上注册得来的777测试商户号
        orderMap.put("accessType", properties.get("union.access.type"));             			  //接入类型，0：直连商户
        orderMap.put("orderId",orderId);             //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则
        try {
            orderMap.put("txnTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(txnTime).getTime() + 15 * 60 * 1000));        //订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
            // 订单超时时间。
            // 超过此时间后，除网银交易外，其他交易银联系统会拒绝受理，提示超时。 跳转银行网银交易如果超时后交易成功，会自动退款，大约5个工作日金额返还到持卡人账户。
            // 此时间建议取支付时的北京时间加15分钟。
            // 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
			orderMap.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss")
					.format(new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(txnTime).getTime()
							+ payOrderTim * 60 * 1000));
        }catch(Exception e) {
            e.printStackTrace();
            logger.error("时间转换错误。"+e);
        }
        orderMap.put("currencyCode", properties.get("union.currency.code"));         			  //交易币种（境内商户一般是156 人民币）
        orderMap.put("txnAmt", BigDecimalUtils.metaConversion(txnAmt));             			      //交易金额，单位分，不要带小数点
        //requestData.put("reqReserved", "透传字段");        		      //请求方保留域，如需使用请启用即可；透传字段（可以实现商户自定义参数的追踪）本交易的后台通知,对本交易的交易状态查询交易、对账文件中均会原样返回，商户可以按需上传，长度为1-1024个字节。出现&={}[]符号时可能导致查询接口应答报文解析失败，建议尽量只传字母数字并使用|分割，或者可以最外层做一次base64编码(base64编码之后出现的等号不会导致解析失败可以不用管)。
        //orderMap.put("riskRateInfo", "{commodityName=测试商品名称}");

        //前台通知地址 （需设置为外网能访问 http https均可），支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址
        //如果想要实现过几秒中自动跳转回商户页面权限，需联系银联业务申请开通自动返回商户权限
        //异步通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知
        orderMap.put("frontUrl", UnionpayConfig.getConfig().getFrontUrl());
        //后台通知地址（需设置为【外网】能访问 http https均可），支付成功后银联会自动将异步通知报文post到商户上送的该地址，失败的交易银联不会发送后台通知
        //后台通知参数详见open.unionpay.com帮助中心 下载  产品接口规范  网关支付产品接口规范 消费交易 商户通知
        //注意:1.需设置为外网能访问，否则收不到通知    2.http https均可  3.收单后台通知后需要10秒内返回http200或302状态码
        //    4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200，那么银联会间隔一段时间再次发送。总共发送5次，每次的间隔时间为0,1,2,4分钟。
        //    5.后台通知地址如果上送了带有？的参数，例如：http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签，否则将会验签失败
        orderMap.put("backUrl", UnionpayConfig.getConfig().getBackUrl());
        //////////////////////////////////////////////////
        //
        //       报文中特殊用法请查看 PCwap网关跳转支付特殊用法.txt
        //
        //////////////////////////////////////////////////
        /**请求参数设置完毕，以下对请求参数进行签名并生成html表单，将表单写入浏览器跳转打开银联页面**/
        Map<String, String>  reqData = AcpService.sign(orderMap,UnionpayConfig.getConfig().getEncoding()); //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
        String requestAppUrl =  UnionpayConfig.getConfig().getAppRequestUrl();  //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
        logger.info("调用银联的接口获取TN信息。入参   reqData："+JSON.toJSONString(reqData));
        Map<String, String>  respData = AcpService.post(reqData,requestAppUrl,UnionpayConfig.getConfig().getEncoding());//发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过
        logger.info("调用银联的接口获取TN信息。出参   rspData："+JSON.toJSONString(respData));
            if(!respData.isEmpty()){
                if(AcpService.validate(respData,  UnionpayConfig.getConfig().getEncoding())){
                    logger.info("验证签名成功。respCode:"+JSON.toJSONString(respData.get("respCode")));
                    String respCode =  respData.get("respCode");
                    if(("00").equals(respCode)){
                        logger.info("最终提交字符串----->" + JSON.toJSONString(respData));
                        return JSONObject.parseObject(JSON.toJSONString(respData));
                    }else{
                        logger.info("银联返回的接口信息状态码错误。respCode："+respCode);
                    }
                }else{
                    logger.info("验证签名失败");
                }
            }else{
                logger.info("未获取到返回报文或返回http状态码非200");
            }
        return null;
    }
    /**
     * Description: 
     * @param:  校验返回参数是否为null
     * @return: 
     * @auther: dengc
     * @date: 2018/8/28 12:33
     */
    public static String checkNull(String str)throws Exception{
        if(StringUtils.isNotBlank(str)){
            str = new String(str.getBytes("ISO-8859-1"),"UTF-8");
        }
        return str;
    }
}
